/*
 * ml20gui - The MSN Webcam Recorder
 * This source code is under the GNU Public License (GPL).
 * Written by Ramiro Polla
 * Initial release: june 3rd, 2005
 *
 * this program records MSN Webcam Video
 */

#include "ml20gui.h"
#include "tlhelp32.h"

codecs_t *codecs;

static codecs_t *dcodecs = 0;
static char wmvcodecpath[MAX_PATH] = { 0 };

extern HMODULE lm20dll;
static msn_t msntc;

static long get_procaddress( char *fname );

static char *look_for_wmv( );
static void kill_codecs( codecs_t *tcodecs );
static msns_t *look_for_msns( );

static void display_msns( HWND hwnd, long index )
{
    char buf[16];
    msns_t *msns;

    if( index < 0 )
        index = 0;

    msns = (msns_t*) SendDlgItemMessage( hwnd, IDC_CPID, CB_GETITEMDATA, index, 0 );
    SendDlgItemMessage( hwnd, IDC_CPID, CB_SETCURSEL, index, 0 );
    if( GetDlgButtonCheck( hwnd, IDC_ML20 ) ) {
        EnableWindow( GetDlgItem(hwnd, IDC_EDIT), TRUE );
        sprintf( buf, "%.8x", (uint32_t) msns->msnt.procaddress );
        SetDlgItemText( hwnd, IDC_EDIT, buf );
    }
}

static void display_codecs( HWND hwnd, codecs_t *tcodecs )
{
    msns_t *msns;

    dcodecs = tcodecs;

    EnableWindow( GetDlgItem(hwnd, IDC_LM20), IS_SET(tcodecs->flags, CODECS_LOCAL) ? TRUE : FALSE );

    EnableWindow( GetDlgItem(hwnd, IDC_ML20), tcodecs->first ? TRUE : FALSE );
    EnableWindow( GetDlgItem(hwnd, IDC_ADDRESS), tcodecs->default_c > 1 ? TRUE : FALSE );
    EnableWindow( GetDlgItem(hwnd, IDC_0X), tcodecs->default_c > 1 ? TRUE : FALSE );
    EnableWindow( GetDlgItem(hwnd, IDC_EDIT), tcodecs->default_c > 1 ? TRUE : FALSE );
    EnableWindow( GetDlgItem(hwnd, IDC_TPID), tcodecs->default_c > 1 ? TRUE : FALSE );
    EnableWindow( GetDlgItem(hwnd, IDC_CPID), tcodecs->default_c > 1 ? TRUE : FALSE );

    CheckDlgButton( hwnd, IDC_LM20, tcodecs->default_c == 1 ? BST_CHECKED : BST_UNCHECKED );
    CheckDlgButton( hwnd, IDC_ML20, tcodecs->default_c > 1 ? BST_CHECKED : BST_UNCHECKED );

    SetDlgItemText( hwnd, IDC_wmv3, wmvcodecpath );

    SendMessage( GetDlgItem(hwnd, IDC_CPID), CB_RESETCONTENT, 0, 0 );

    if( tcodecs->first ) {
        for( msns = tcodecs->first ; msns ; msns = msns->next ) {
            char buf[16];
            int index;
            sprintf( buf, "%.8x", (uint32_t) msns->msnt.pid );
            index = SendMessage( GetDlgItem(hwnd, IDC_CPID), CB_ADDSTRING, 0, (LPARAM) buf );
            SendMessage( GetDlgItem(hwnd, IDC_CPID), CB_SETITEMDATA, index, (LPARAM) msns );
        }

        display_msns( hwnd, tcodecs->default_c - 2 );
    }
}

LRESULT CALLBACK CodecsProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    int buflen = sizeof(wmvcodecpath);

    switch(msg) {
        case WM_INITDIALOG:
            SetWindowText( hwnd, str_codecs );
            SetDlgItemText( hwnd, IDOK, str_ok );
            SetDlgItemText( hwnd, IDCANCEL, str_cancel );
            SetDlgItemText( hwnd, IDDEFAULT, str_default );
            SetDlgItemText( hwnd, IDSEARCH, str_search );
            SetDlgItemText( hwnd, IDC_LM20, str_lm20 );
            SetDlgItemText( hwnd, IDC_ML20, str_ml20 );
            SetDlgItemText( hwnd, IDC_TPID, str_pid );
            SetDlgItemText( hwnd, IDC_ADDRESS, str_address );

            reg_read_string( "WMVCodec", (uint8_t*) wmvcodecpath, &buflen );
            display_codecs( hwnd, codecs );

            break;
        case WM_CLOSE:
            EndDialog( hwnd, 0 );
            break;
        case WM_COMMAND:
        {
            switch(LOWORD(wParam)) {
            case IDOK:
                if( dcodecs != codecs ) {
                    kill_codecs( codecs );
                    codecs = dcodecs;
                }

                if( GetDlgButtonCheck( hwnd, IDC_LM20 ) ) {
                    codecs->default_c = 1;
                } else if( GetDlgButtonCheck( hwnd, IDC_ML20 ) ) {
                    int index = SendDlgItemMessage( hwnd, IDC_CPID, CB_GETCURSEL, 0, 0 );
                    msns_t *msns = (msns_t*) SendDlgItemMessage( hwnd, IDC_CPID, CB_GETITEMDATA, index, 0 );

                    codecs->default_c = index + 2;
                    {
                        char buf[16];
                        uint32_t x = 0;

                        GetDlgItemText( hwnd, IDC_EDIT, buf, 16 );
                        sscanf( buf, "%x", &x );
                        msns->msnt.procaddress = x;
                    }
                }

                GetDlgItemText( hwnd, IDC_wmv3, wmvcodecpath, sizeof(wmvcodecpath) );
                reg_write_string( "WMVCodec", (uint8_t*) wmvcodecpath, strlen(wmvcodecpath)+1 );

                EndDialog( hwnd, 0 );
                break;
            case IDDEFAULT:
                if( dcodecs != codecs )
                    kill_codecs( dcodecs );
                reg_read_string( "WMVCodec", (uint8_t*) wmvcodecpath, &buflen );
                display_codecs( hwnd, codecs );
                break;
            case IDCANCEL:
                if( dcodecs != codecs )
                    kill_codecs( dcodecs );
                EndDialog( hwnd, 0 );
                break;
            case IDSEARCH:
                {
                    codecs_t *tcodecs;

                    SetCursor( LoadCursor( NULL, IDC_WAIT ) );
                    ShowCursor( TRUE );

                    tcodecs = new_codecs( );
                    tcodecs->first = look_for_msns( );
                    look_for_wmv( );

                    if( IS_SET(tcodecs->flags, CODECS_LOCAL) )
                        codecs->default_c = 1;
                    else if( tcodecs->first )
                        codecs->default_c = 2;
                    else
                        //absolutely no codecs!
                        codecs->default_c = 0;

                    if( dcodecs != codecs )
                        kill_codecs( dcodecs );

                    display_codecs( hwnd, tcodecs );

                    ShowCursor( FALSE );
                    SetCursor( LoadCursor( NULL, IDC_ARROW ) );
                }
                break;
            case IDC_CPID:
                if( HIWORD(wParam) == CBN_SELCHANGE )
                    display_msns( hwnd, SendMessage( (HWND) lParam, CB_GETCURSEL, 0, 0 ) );
                break;
            case IDC_ML20:
                EnableWindow( GetDlgItem(hwnd, IDC_ADDRESS), TRUE );
                EnableWindow( GetDlgItem(hwnd, IDC_0X), TRUE );
                EnableWindow( GetDlgItem(hwnd, IDC_EDIT), TRUE );
                EnableWindow( GetDlgItem(hwnd, IDC_TPID), TRUE );
                EnableWindow( GetDlgItem(hwnd, IDC_CPID), TRUE );
                display_msns( hwnd, SendMessage( GetDlgItem( hwnd, IDC_CPID ), CB_GETCURSEL, 0, 0 ) );
                break;
            case IDC_LM20:
                EnableWindow( GetDlgItem(hwnd, IDC_ADDRESS), FALSE );
                EnableWindow( GetDlgItem(hwnd, IDC_0X), FALSE );
                EnableWindow( GetDlgItem(hwnd, IDC_EDIT), FALSE );
                EnableWindow( GetDlgItem(hwnd, IDC_TPID), FALSE );
                EnableWindow( GetDlgItem(hwnd, IDC_CPID), FALSE );
                break;
            case IDC_BROWSE:
                {
                    char *fname;
                    if( (fname = OpenFileDlg( hwnd,
                        "wmv9vcm.dll\0*.dll\0",
                        "dll" )) )
                    {
                        strcpy( wmvcodecpath, fname );
                        SetDlgItemText( hwnd, IDC_wmv3, wmvcodecpath );
                    }
                }
                break;
            }
        }
        default:
            return FALSE;
    }
    return TRUE;
}

codecs_t *new_codecs( void )
{
    codecs_t *tcodecs;

    tcodecs = calloc(1, sizeof(codecs_t));

    msntc.rp = lm20dll;

    if( (msntc.procaddress = (DWORD) GetProcAddress( lm20dll, "DriverProc" )) ) {
        SET_BIT( tcodecs->flags, CODECS_LOCAL );
        tcodecs->default_c = 1;
    }

    return tcodecs;
}

static void kill_codecs( codecs_t *tcodecs )
{
    msns_t *msns;

    if( tcodecs->first ) {
        msns_t *tmsns;
        for( msns = tcodecs->first ; msns->next ; tmsns = msns, msns = msns->next, free(tmsns) );
        free( msns );
    }

    free( tcodecs );
}

//look for WMV codec
static char *look_for_wmv( )
{
    HANDLE tool;
    PROCESSENTRY32 pe;

    tool = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
    pe.dwSize = sizeof(PROCESSENTRY32);

    Process32First( tool, &pe );
    do {
        if( !strcmp( pe.szExeFile, "msnmsgr.exe" ) ) {
            char *fname;
            char fname2[MAX_PATH];

            fname = get_pid_filename( pe.th32ProcessID );
            strcpy( fname2, fname );
            PathRemoveFileSpec( fname2 );
            PathAppend( fname2, "wmv9vcm.dll" );
            free(fname);
            if( PathFileExists( fname2 ) )
                strcpy( wmvcodecpath, fname2 );
            break;
        }
    }
    while( Process32Next( tool, &pe ) );

    CloseHandle( tool );

    return (char*) NULL;
}

//look for MSN Messenger
static msns_t *look_for_msns( )
{
    HANDLE tool = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
    PROCESSENTRY32 pe;
    msns_t *first = 0;

    pe.dwSize = sizeof(PROCESSENTRY32);

    Process32First( tool, &pe );
    do {
        if( !strcmp( pe.szExeFile, "msnmsgr.exe" ) ) {
            char *fname;
            msns_t *msns;

            if( !first )
                msns = first = calloc( 1, sizeof(msns_t) );
            else {
                for( msns = first ; msns->next ; msns = msns->next );
                msns->next = calloc( 1, sizeof(msns_t) );
                msns = msns->next;
            }

            msns->msnt.pid = pe.th32ProcessID;

            fname = get_pid_filename( pe.th32ProcessID );
            msns->msnt.procaddress = get_procaddress( fname );
            free(fname);
        }
    }
    while( Process32Next( tool, &pe ) );

    CloseHandle( tool );

    return first;
}

static long get_procaddress( char *fname )
{
    FILE *tmp;
    char mlstring[] = { 'M', 'L', '2', '0', 'h', 'v', 'i', 'd', 'c' };
    DWORD proc = 0;

    tmp = fopen( fname, "rb" );
    if( tmp ) {
        char buf[sizeof(mlstring)];
        char c;
        while( !feof( tmp ) ) {
            if( (c = getc( tmp )) == 'h' ) {
                int curpos = ftell( tmp );
                fread( buf, 1, sizeof(mlstring), tmp );

                if( !memcmp( buf, mlstring, sizeof(mlstring) ) ) {
                    fseek( tmp, curpos-5, SEEK_SET );
                    fread( &proc, 1, sizeof(proc), tmp );
                }
                fseek( tmp, curpos, SEEK_SET );
            }
        }
        fclose( tmp );
    }

    return proc;
}

msn_t *get_default_codec( int flags )
{
    msn_t *ret;

    if( IS_SET( flags, VSTREAM_VC ) ) {
        int buflen = sizeof(wmvcodecpath);
        if( !reg_read_string( "WMVCodec", (uint8_t*) wmvcodecpath, &buflen ) ) {
            look_for_wmv( );
            reg_write_string( "WMVCodec", (uint8_t*) wmvcodecpath, strlen(wmvcodecpath)+1 );
        }
        msntc.pid = 0;
        msntc.wmv = wmvcodecpath[0] ? wmvcodecpath : NULL;
        ret = &msntc;
    }
    else if( !(codecs->default_c) )
        ret = (msn_t *) 0;
    else if( codecs->default_c == 1 ) {
        msntc.pid = 0;
        msntc.wmv = NULL;
        ret = &msntc;
    }
    else {
        msns_t *msns;
        int i = codecs->default_c - 2;

        for( msns = codecs->first ; i ; i--, msns = msns->next );
        ret = &msns->msnt;
    }
    return ret;
}
